<?
/**
 * Copyright 2007 Melange.
 *
 * This file is part of PHP-AAS.
 *
 * PHP-AAS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP-AAS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP-AAS; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category    Melange
 * @package     php-aas
 * @subpackage  realm
 * @copyright   Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @author      Jeroen Simons <jeroen@melange.nl>
 * @link        http://www.melange.nl/
 *
 */


/**
 * Simple implementation of <b>Realm</b> that reads an XML file to configure
 * the valid users, passwords, and roles.  The file format (and default file
 * location) are identical to those currently supported by Tomcat 3.X.
 */
abstract class RealmBase {


    /**
     * Logger
     */
//    private $log;


    // ----------------------------------------------------- Instance Variables


    /**
     * The Container with which this Realm is associated.
     */
    protected $container = null;


    /**
     * Digest algorithm used in storing passwords in a non-plaintext format.
     * Valid values are those accepted for the algorithm name by the
     * MessageDigest class, or <code>null</code> if no digesting should
     * be performed.
     */
    protected $digest = null;


    /**
     * The MessageDigest object for digesting user credentials (passwords).
     */
    protected $md = null;


    /**
     * Has this component been started?
     */
    protected $started = false;



    // ------------------------------------------------------------- Properties


    public function __construct() {

        $this->log = LoggerManager::getLogger("RealmBase");

    }


    /**
     * Return the Container with which this Realm has been associated.
     */
    public function getContainer() {
        return ($this->container);
    }


    /**
     * Set the Container with which this Realm has been associated.
     *
     * @param container The associated Container
     */
    public function setContainer(Container $container) {
        $this->container = container;
    }


    /**
     * Return the ApplicationContext with which this Realm has been associated.
     */
    public function getApplicationContext() {
        return ($this->applicationContext);
    }


    /**
     * Set the ApplicationContext with which this Realm has been associated.
     *
     * @param applicationContext The associated ApplicationContext
     */
    public function setApplicationContext(Context $applicationContext) {
        $this->applicationContext = $applicationContext;
    }


    /**
     * Return the digest algorithm  used for storing credentials.
     */
    public function getDigest() {
        return $this->digest;
    }


    /**
     * Set the digest algorithm used for storing credentials.
     *
     * @param digest The new digest algorithm
     */
    public function setDigest($digest) {

        $this->digest = $digest;

        // Create a MessageDigest instance for credentials, if desired
        if ($this->digest !== null) {
            $this->md = $this->digest;
        }

    }


    /**
     * Return the "validate certificate chains" flag.
     */
    public function getValidate() {
        return ($this->validate);
    }


    /**
     * Set the "validate certificate chains" flag.
     *
     * @param validate The new validate certificate chains flag
     */
    public function setValidate($validate) {
        $this->validate = $validate;
    }


    // --------------------------------------------------------- Public Methods


    /**
     * Return the Principal associated with the specified username and
     * credentials, if there is one; otherwise return <code>null</code>.
     *
     * @param username Username of the Principal to look up
     * @param credentials Password or other credentials to use in
     *  authenticating this username
     */
    public function authenticate($username, $credentials) {

        $serverCredentials = $this->getPassword($username);

        $validated;
        if ( $serverCredentials === null ) {
            $validated = false;
        } else if ($this->hasMessageDigest()) {
            $validated = strtolower($serverCredentials) ==
                strtolower($this->digest($credentials));
        } else {
            $validated = $serverCredentials == $credentials;
        }

        if(!$validated ) {
            return null;
        }

        return $this->getPrincipal($username);

    }


    /**
     * Return <code>true</code> if the specified Principal has the specified
     * security role, within the context of this Realm; otherwise return
     * <code>false</code>.  This method can be overridden by Realm
     * implementations, but the default is adequate when an instance of
     * <code>GenericPrincipal</code> is used to represent authenticated
     * Principals from this Realm.
     *
     * @param principal Principal for whom the role is to be checked
     * @param role Security role to be checked
     */
    public function hasRole(GenericPrincipal $principal, $role) {

        // Should be overriten in JAASRealm - to avoid pretty inefficient conversions
        if (($principal === null) || ($role === null))
            return (false);

        // Todo: Not sure what this does ??
        if (!($principal->getRealm() instanceof $this)) {
            $this->log->warn("Different realm " . $this->getName() . " " . $principal->getRealm()->getName());
        }

        $result = $principal->hasRole($role);

        if ($this->log->isDebugEnabled()) {
            $name = $principal->getName();
//            if ($result) {
////                if($this->log->isDebugEnabled())
////                    $this->log->debug("RealmBase : hasRole : Success : ".$name." : ".$role);
//            } else {
//                if($this->log->isDebugEnabled())
//                    $this->log->debug("RealmBase : hasRole : Failure : ".$name." : ".$role);
//            }
        }
        return ($result);

    }


    // ------------------------------------------------------ Protected Methods


    /**
     * Digest the password using the specified algorithm and
     * convert the result to a corresponding hexadecimal string.
     * If exception, the plain credentials string is returned.
     *
     * <strong>IMPLEMENTATION NOTE</strong> - This implementation is
     * synchronized because it reuses the MessageDigest instance.
     * This should be faster than cloning the instance on every request.
     *
     * @param credentials Password or other credentials to use in
     *  authenticating this username
     */
    protected function digest($credentials)  {

        // If no MessageDigest instance is specified, return unchanged
        if ($this->hasMessageDigest() === false)
            return ($credentials);

        return md5($credentials);

    }


    protected function hasMessageDigest() {
        return ($this->md !== null);
    }


    /**
     * Return a short name for this Realm implementation, for use in
     * log messages.
     */
    protected abstract function getName();


    /**
     * Return the password associated with the given principal's user name.
     */
    protected abstract function getPassword($username);


    /**
     * Return the Principal associated with the given user name.
     */
    protected abstract function getPrincipal($username);


}


class GenericPrincipal {


    // ----------------------------------------------------- Instance Variables


    /**
     * The username of the user represented by this Principal.
     */
    protected $name = null;


    /**
     * The authentication credentials for the user represented by this
     * Principal.
     */
    protected $password = null;


    /**
     * The set of roles associated with this user.
     */
    protected $roles = array();


    // ----------------------------------------------------------- Constructors


    /**
     * Construct a new Principal, associated with the specified Realm, for the
     * specified username and password, with the specified role names
     * (as Strings).
     *
     * @param realm The Realm that owns this principal
     * @param name The username of the user represented by this Principal
     * @param password Credentials used to authenticate this user
     * @param roles List of roles (must be Strings) possessed by this user
     */
    public function __construct(RealmBase $realm, $name, $password, $roles=null) {
        $this->realm = $realm;
        $this->name = $name;
        $this->password = $password;
        if ($roles !== null) {
            $this->roles = $roles;
        }
    }


    // ------------------------------------------------------------- Properties


    public function getRealm() {
        return ($this->realm);
    }

    public function getName() {
        return ($this->name);
    }

    public function getPassword() {
        return ($this->password);
    }

    public function getRoles() {
        return ($this->roles);
    }

    /**
     * Does the user represented by this Principal possess the specified role?
     *
     * @param role Role to be tested
     */
    public function hasRole($role) {
        if ("*" == $role)
            return true;
        if ($role === null)
            return (false);
        return in_array($role, $this->roles);

    }

    /**
     * Return a String representation of this object, which exposes only
     * information that should be public.
     */
    public function __toString() {

        $sb = "GenericPrincipal[";
        $sb .= "name = " . $this->name . ",";
        $sb .= "password = " . $this->password . ",";
        $sb .= "(";
        for ($i = 0; $i < count($this->roles); $i++) {
            $sb .= $this->roles[$i].",";
        }
        $sb .= ")]";
        return ($sb);

    }

    public function __clone() {
        $this->realm = clone $this->realm;
        $this->password = null;
    }

}

?>